<html>
<article itemscope="itemscope" itemtype="http://schema.org/Article" data-entry-id="7216604684035325989"
         data-draft-id="7216604612404953146" data-original-type="0" class="article" data-v-61fb5e44=""
         data-v-539963b4=""><!---->
    <meta itemprop="headline" content="魔改xxl-job，彻底告别手动配置任务！">
    <meta itemprop="keywords" content="后端,Java,掘金·金石计划">
    <meta itemprop="datePublished" content="2023-03-31T08:38:34.000Z">
    <meta itemprop="image"
          content="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/icon/icon-128.png~tplv-t2oaga2asx-image.image">
    <div itemprop="author" itemscope="itemscope" itemtype="http://schema.org/Person">
        <meta itemprop="name" content="码农参上">
        <meta itemprop="url" content="https://juejin.cn/user/3853127158081144">
    </div>
    <div itemprop="publisher" itemscope="itemscope" itemtype="http://schema.org/Organization">
        <meta itemprop="name" content="掘金">
        <div itemprop="logo" itemscope="itemscope" itemtype="https://schema.org/ImageObject">
            <meta itemprop="url"
                  content="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-assets/icon/icon-white-180.png~tplv-t2oaga2asx-image.image">
            <meta itemprop="width" content="180">
            <meta itemprop="height" content="180">
        </div>
    </div>
    <h1 class="article-title" data-v-61fb5e44="">
        魔改xxl-job，彻底告别手动配置任务！
        <!----> <!----></h1>
    <div class="author-info-block" data-v-61fb5e44="">
        <div class="author-info-box" data-v-61fb5e44="">
            <div class="author-name" data-v-61fb5e44=""><a href="/user/3853127158081144/posts" target="_blank" rel=""
                                                           class="jj-link username username ellipsis" data-v-65b50b51=""
                                                           data-v-292f6e48="" data-v-61fb5e44=""><span class="name"
                                                                                                       style="max-width:160px;"
                                                                                                       data-v-65b50b51=""
                                                                                                       data-v-292f6e48="">
    码农参上
  </span> <!----> <!----> <!----> </a></div>
            <div class="meta-box" data-v-61fb5e44="">
                <time datetime="2023-03-31T08:38:34.000Z" title="Fri Mar 31 2023 16:38:34 GMT+0800 (中国标准时间)"
                      class="time" data-v-61fb5e44="">
                    2023-03-31
                </time>
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"
                     class="read-icon" data-v-61fb5e44="">
                    <path d="M7.90078 2.80078C4.49278 2.80078 1.74745 6.11672 0.800781 7.77469C1.74745 9.58339 4.49278 13.2008 7.90078 13.2008C11.3088 13.2008 14.0541 9.58339 15.0008 7.77469C14.0541 6.11672 11.3088 2.80078 7.90078 2.80078Z"
                          stroke="currentColor" data-v-61fb5e44=""></path>
                    <circle cx="7.89922" cy="8.00078" r="2.2" stroke="currentColor" data-v-61fb5e44=""></circle>
                </svg>
                <span class="views-count" data-v-61fb5e44="">
                    15,466
                  </span> <span class="read-time" data-v-61fb5e44=""><svg width="16" height="16" viewBox="0 0 16 16"
                                                                          fill="none" xmlns="http://www.w3.org/2000/svg"
                                                                          data-v-61fb5e44="" class=""><rect width="16"
                                                                                                            height="16"
                                                                                                            fill="none"
                                                                                                            data-v-61fb5e44=""></rect><circle
                    cx="8" cy="8" r="5.65625" stroke="#8A919F" data-v-61fb5e44=""></circle><path
                    d="M7.69141 5.18652V8.30924H10.8141" stroke="#8A919F" stroke-linecap="round" stroke-linejoin="round"
                    data-v-61fb5e44=""></path></svg>
                    阅读9分钟
                  </span>
                <div data-v-61fb5e44="" class="first-column">
                    <div data-v-61fb5e44="" class="gap"></div>
                    <div data-v-61fb5e44="" class="word">
                        <svg data-v-61fb5e44="" width="14" height="13" viewBox="0 0 14 13" fill="none"
                             xmlns="http://www.w3.org/2000/svg" class="">
                            <path data-v-61fb5e44="" fill-rule="evenodd" clip-rule="evenodd"
                                  d="M1.60043 0.961914C1.09407 0.961914 0.683594 1.37239 0.683594 1.87875V11.6807C0.683594 12.1871 1.09407 12.5976 1.60042 12.5976H3.6425C4.14885 12.5976 4.55933 12.1871 4.55933 11.6807V1.87875C4.55933 1.37239 4.14885 0.961914 3.6425 0.961914H1.60043ZM1.70043 1.97875H3.5425V11.5807H1.70042L1.70043 1.97875Z"
                                  fill="#8A919F"></path>
                            <path data-v-61fb5e44="" fill-rule="evenodd" clip-rule="evenodd"
                                  d="M4.45589 0.961914C3.94954 0.961914 3.53906 1.37239 3.53906 1.87875V11.6807C3.53906 12.1871 3.94954 12.5976 4.45589 12.5976H6.49797C7.00432 12.5976 7.4148 12.1871 7.4148 11.6807V1.87875C7.4148 1.37239 7.00432 0.961914 6.49797 0.961914H4.45589ZM4.55589 1.97875H6.39797V11.5807H4.55589L4.55589 1.97875Z"
                                  fill="#8A919F"></path>
                            <path data-v-61fb5e44="" fill-rule="evenodd" clip-rule="evenodd"
                                  d="M7.48055 1.42132C6.99145 1.55238 6.7012 2.05511 6.83225 2.54421L9.36919 12.0122C9.50024 12.5013 10.003 12.7915 10.4921 12.6605L12.4646 12.132C12.9537 12.0009 13.2439 11.4982 13.1129 11.0091L10.5759 1.54109C10.4449 1.052 9.94214 0.761742 9.45304 0.892796L7.48055 1.42132ZM7.84031 2.37763L9.61963 1.90086L12.1048 11.1757L10.3255 11.6524L7.84031 2.37763Z"
                                  fill="#8A919F"></path>
                        </svg>
                        专栏：&nbsp;
                    </div>
                    <div data-v-61fb5e44="" class="title">
                        Java实战
                    </div>
                </div>
            </div>
        </div>
        <div style="flex:1;" data-v-61fb5e44=""></div> <!---->
        <button data-v-b60b2868="" data-v-61fb5e44="" class="jj-follow-button follow"><span data-v-b60b2868=""
                                                                                            class="text">关注</span>
        </button>
    </div> <!----> <!----> <!----> <img data-v-61fb5e44=""
                                        src="https://p3-piu.byteimg.com/tos-cn-i-8jisjyls3a/b37ce6cd3dfa46f699d8fc9c7c888f2f~tplv-8jisjyls3a-3:0:0:q75.png"
                                        style="width: 100%; height: auto; display: block;">
    <div id="article-root" itemprop="articleBody" class="main" data-v-61fb5e44="">
        <div class="article-viewer markdown-body result">
            <style>.markdown-body {
                word-break: break-word;
                line-height: 1.75;
                font-weight: 400;
                font-size: 16px;
                overflow-x: hidden;
                color: #252933
            }

            .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
                line-height: 1.5;
                margin-top: 35px;
                margin-bottom: 10px;
                padding-bottom: 5px
            }

            .markdown-body h1 {
                font-size: 24px;
                line-height: 38px;
                margin-bottom: 5px
            }

            .markdown-body h2 {
                font-size: 22px;
                line-height: 34px;
                padding-bottom: 12px;
                border-bottom: 1px solid #ececec
            }

            .markdown-body h3 {
                font-size: 20px;
                line-height: 28px
            }

            .markdown-body h4 {
                font-size: 18px;
                line-height: 26px
            }

            .markdown-body h5 {
                font-size: 17px;
                line-height: 24px
            }

            .markdown-body h6 {
                font-size: 16px;
                line-height: 24px
            }

            .markdown-body p {
                line-height: inherit;
                margin-top: 22px;
                margin-bottom: 22px
            }

            .markdown-body img {
                max-width: 100%
            }

            .markdown-body hr {
                border: none;
                border-top: 1px solid #ddd;
                margin-top: 32px;
                margin-bottom: 32px
            }

            .markdown-body code {
                word-break: break-word;
                border-radius: 2px;
                overflow-x: auto;
                background-color: #fff5f5;
                color: #ff502c;
                font-size: .87em;
                padding: .065em .4em
            }

            .markdown-body code, .markdown-body pre {
                font-family: Menlo, Monaco, Consolas, Courier New, monospace
            }

            .markdown-body pre {
                overflow: auto;
                position: relative;
                line-height: 1.75
            }

            .markdown-body pre > code {
                font-size: 12px;
                padding: 15px 12px;
                margin: 0;
                word-break: normal;
                display: block;
                overflow-x: auto;
                color: #333;
                background: #f8f8f8
            }

            .markdown-body a {
                text-decoration: none;
                color: #0269c8;
                border-bottom: 1px solid #d1e9ff
            }

            .markdown-body a:active, .markdown-body a:hover {
                color: #275b8c
            }

            .markdown-body table {
                display: inline-block !important;
                font-size: 12px;
                width: auto;
                max-width: 100%;
                overflow: auto;
                border: 1px solid #f6f6f6
            }

            .markdown-body thead {
                background: #f6f6f6;
                color: #000;
                text-align: left
            }

            .markdown-body tr:nth-child(2n) {
                background-color: #fcfcfc
            }

            .markdown-body td, .markdown-body th {
                padding: 12px 7px;
                line-height: 24px
            }

            .markdown-body td {
                min-width: 120px
            }

            .markdown-body blockquote {
                color: #666;
                padding: 1px 23px;
                margin: 22px 0;
                border-left: 4px solid #cbcbcb;
                background-color: #f8f8f8
            }

            .markdown-body blockquote:after {
                display: block;
                content: ""
            }

            .markdown-body blockquote > p {
                margin: 10px 0
            }

            .markdown-body ol, .markdown-body ul {
                padding-left: 28px
            }

            .markdown-body ol li, .markdown-body ul li {
                margin-bottom: 0;
                list-style: inherit
            }

            .markdown-body ol li .task-list-item, .markdown-body ul li .task-list-item {
                list-style: none
            }

            .markdown-body ol li .task-list-item ol, .markdown-body ol li .task-list-item ul, .markdown-body ul li .task-list-item ol, .markdown-body ul li .task-list-item ul {
                margin-top: 0
            }

            .markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul {
                margin-top: 3px
            }

            .markdown-body ol li {
                padding-left: 6px
            }

            .markdown-body .contains-task-list {
                padding-left: 0
            }

            .markdown-body .task-list-item {
                list-style: none
            }

            @media (max-width: 720px) {
                .markdown-body h1 {
                    font-size: 24px
                }

                .markdown-body h2 {
                    font-size: 20px
                }

                .markdown-body h3 {
                    font-size: 18px
                }
            }</style>
            <style data-highlight="" data-highlight-key="juejin">.markdown-body pre, .markdown-body pre > code.hljs {
                color: #333;
                background: #f8f8f8
            }

            .hljs-comment, .hljs-quote {
                color: #998;
                font-style: italic
            }

            .hljs-keyword, .hljs-selector-tag, .hljs-subst {
                color: #333;
                font-weight: 700
            }

            .hljs-literal, .hljs-number, .hljs-tag .hljs-attr, .hljs-template-variable, .hljs-variable {
                color: teal
            }

            .hljs-doctag, .hljs-string {
                color: #d14
            }

            .hljs-section, .hljs-selector-id, .hljs-title {
                color: #900;
                font-weight: 700
            }

            .hljs-subst {
                font-weight: 400
            }

            .hljs-class .hljs-title, .hljs-type {
                color: #458;
                font-weight: 700
            }

            .hljs-attribute, .hljs-name, .hljs-tag {
                color: navy;
                font-weight: 400
            }

            .hljs-link, .hljs-regexp {
                color: #009926
            }

            .hljs-bullet, .hljs-symbol {
                color: #990073
            }

            .hljs-built_in, .hljs-builtin-name {
                color: #0086b3
            }

            .hljs-meta {
                color: #999;
                font-weight: 700
            }

            .hljs-deletion {
                background: #fdd
            }

            .hljs-addition {
                background: #dfd
            }

            .hljs-emphasis {
                font-style: italic
            }

            .hljs-strong {
                font-weight: 700
            }</style>
            <p>哈喽大家好啊，我是Hydra。</p>
            <p>
                xxl-job是一款非常优秀的任务调度中间件，轻量级、使用简单、支持分布式等优点，让它广泛应用在我们的项目中，解决了不少定时任务的调度问题。</p>
            <p>
                我们都知道，在使用过程中需要先到xxl-job的任务调度中心页面上，配置<strong>执行器executor</strong>和具体的<strong>任务job</strong>，这一过程如果项目中的定时任务数量不多还好说，如果任务多了的话还是挺费工夫的。
            </p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7aa8ce0b22674f4ca8c806276ccc24c5~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>假设项目中有上百个这样的定时任务，那么每个任务都需要走一遍绑定<code>jobHander</code>后端接口，填写<code>cron</code>表达式这个流程…
            </p>
            <p>我就想问问，填多了谁能不迷糊？</p>
            <p>于是出于功能优化（
                <del>偷懒</del>
                ）这一动机，前几天我萌生了一个想法，有没有什么方法能够告别xxl-job的管理页面，能够让我不再需要到页面上去手动注册执行器和任务，实现让它们自动注册到调度中心呢。
            </p>
            <h2 data-id="heading-0">分析</h2>
            <p>分析一下，其实我们要做的很简单，只要在项目启动时主动注册<code>executor</code>和各个<code>jobHandler</code>到调度中心就可以了，流程如下：
            </p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bac99a7fd47d47b2ae1e075da5b692e8~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>
                有的小伙伴们可能要问了，我在页面上创建<strong>执行器</strong>的时候，不是有一个选项叫做<strong>自动注册</strong>吗，为什么我们这里还要自己添加新执行器？
            </p>
            <p>其实这里有个误区，这里的自动注册指的是会根据项目中配置的<code>xxl.job.executor.appname</code>，将配置的机器地址自动注册到这个执行器的地址列表中。但是如果你之前没有手动创建过执行器，那么是不会给你自动添加一个新执行器到调度中心的。
            </p>
            <p>既然有了想法咱们就直接开干，先到github上拉一份xxl-job的源码下来：</p>
            <blockquote>
                <p>
                    <a href="https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fxuxueli%2Fxxl-job%2Fhttps%3A%2F%2Fgithub.com%2Fxuxueli%2Fxxl-job%2F"
                       target="_blank" title="https://github.com/xuxueli/xxl-job/https://github.com/xuxueli/xxl-job/"
                       ref="nofollow noopener noreferrer">github.com/xuxueli/xxl…</a></p>
            </blockquote>
            <p>整个项目导入idea后，先看一下结构：</p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e23a017575824e499ae312c2c498cb9e~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>结合着文档和代码，先梳理一下各个模块都是干什么的：</p>
            <ul>
                <li><code>xxl-job-admin</code>：任务调度中心，启动后就可以访问管理页面，进行执行器和任务的注册、以及任务调用等功能了
                </li>
                <li><code>xxl-job-core</code>：公共依赖，项目中使用到xxl-job时要引入的依赖包</li>
                <li><code>xxl-job-executor-samples</code>：执行示例，分别包含了springboot版本和不使用框架的版本</li>
            </ul>
            <p>为了弄清楚注册和查询<code>executor</code>和<code>jobHandler</code>调用的是哪些接口，我们先从页面上去抓一个请求看看：
            </p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/81d7b56a8e43410d8d96889a843c2277~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>好了，这样就能定位到<code>xxl-job-admin</code>模块中<code>/jobgroup/save</code>这个接口，接下来可以很容易地找到源码位置：
            </p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dd1e48a3e72d405bb001177df77bb2c2~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>按照这个思路，可以找到下面这几个关键接口：</p>
            <ul>
                <li><code>/jobgroup/pageList</code>：执行器列表的条件查询</li>
                <li><code>/jobgroup/save</code>：添加执行器</li>
                <li><code>/jobinfo/pageList</code>：任务列表的条件查询</li>
                <li><code>/jobinfo/add</code>：添加任务</li>
            </ul>
            <p>但是如果直接调用这些接口，那么就会发现它会跳转到<code>xxl-job-admin</code>的的登录页面：</p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab2f6695fa3b43ea86e772c422d6b50f~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>其实想想也明白，出于安全性考虑，调度中心的接口也不可能允许裸调的。那么再回头看一下刚才页面上的请求就会发现，它在<code>Headers</code>中添加了一条名为<code>XXL_JOB_LOGIN_IDENTITY</code>的<code>cookie</code>：
            </p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/35f7a395039d4c80a229eb7920bd70ab~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>
                至于这条<code>cookie</code>，则是在通过用户名和密码调用调度中心的<code>/login</code>接口时返回的，在返回的<code>response</code>可以直接拿到。只要保存下来，并在之后每次请求时携带，就能够正常访问其他接口了。
            </p>
            <p>到这里，我们需要的5个接口就基本准备齐了，接下来准备开始正式的改造工作。</p>
            <h2 data-id="heading-1">改造</h2>
            <p>
                我们改造的目的是实现一个<code>starter</code>，以后只要引入这个<code>starter</code>就能实现<code>executor</code>和<code>jobHandler</code>的自动注册，要引入的关键依赖有下面两个：
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn">
</div><span class="code-block-extension-lang">xml</span></div></div><code
                    class="hljs language-xml code-block-extension-codeShowNum" lang="xml"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-tag">&lt;<span
                    class="hljs-name">dependency</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="2">    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.xuxueli<span
        class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="3">    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>xxl-job-core<span
        class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="4">    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.3.0<span
        class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="5"><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="6"></span>
<span class="code-block-extension-codeLine" data-line-num="7"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="8">    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span
        class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="9">    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-autoconfigure<span
        class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="10"><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span></span>
</code></pre>
            <h3 data-id="heading-2">1、接口调用</h3>
            <p>
                在调用调度中心的接口前，先把<code>xxl-job-admin</code>模块中的<code>XxlJobInfo</code>和<code>XxlJobGroup</code>这两个类拿到我们的starter项目中，用于接收接口调用的结果。
            </p>
            <h4 data-id="heading-3">登录接口</h4>
            <p>
                创建一个<code>JobLoginService</code>，在调用业务接口前，需要通过登录接口获取<code>cookie</code>，并在获取到<code>cookie</code>后，缓存到本地的<code>Map</code>中。
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Map&lt;String,String&gt; loginCookie=<span
                    class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span>&lt;&gt;();</span>
<span class="code-block-extension-codeLine" data-line-num="2"></span>
<span class="code-block-extension-codeLine" data-line-num="3"><span class="hljs-keyword">public</span> <span
        class="hljs-keyword">void</span> <span class="hljs-title function_">login</span><span
        class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="4">    String url=adminAddresses+<span class="hljs-string">"/login"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="5">    <span class="hljs-type">HttpResponse</span> <span
        class="hljs-variable">response</span> <span class="hljs-operator">=</span> HttpRequest.post(url)</span>
<span class="code-block-extension-codeLine" data-line-num="6">            .form(<span
        class="hljs-string">"userName"</span>,username)</span>
<span class="code-block-extension-codeLine" data-line-num="7">            .form(<span
        class="hljs-string">"password"</span>,password)</span>
<span class="code-block-extension-codeLine" data-line-num="8">            .execute();</span>
<span class="code-block-extension-codeLine" data-line-num="9">    List&lt;HttpCookie&gt; cookies = response.getCookies();</span>
<span class="code-block-extension-codeLine" data-line-num="10">    Optional&lt;HttpCookie&gt; cookieOpt = cookies.stream()</span>
<span class="code-block-extension-codeLine" data-line-num="11">            .filter(cookie -&gt; cookie.getName().equals(<span
        class="hljs-string">"XXL_JOB_LOGIN_IDENTITY"</span>)).findFirst();</span>
<span class="code-block-extension-codeLine" data-line-num="12">    <span class="hljs-keyword">if</span> (!cookieOpt.isPresent())</span>
<span class="code-block-extension-codeLine" data-line-num="13">        <span class="hljs-keyword">throw</span> <span
        class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span
        class="hljs-string">"get xxl-job cookie error!"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="14"></span>
<span class="code-block-extension-codeLine" data-line-num="15">    <span class="hljs-type">String</span> <span
        class="hljs-variable">value</span> <span class="hljs-operator">=</span> cookieOpt.get().getValue();</span>
<span class="code-block-extension-codeLine" data-line-num="16">    loginCookie.put(<span class="hljs-string">"XXL_JOB_LOGIN_IDENTITY"</span>,value);</span>
<span class="code-block-extension-codeLine" data-line-num="17">}</span>
</code></pre>
            <p>其他接口在调用时，直接从缓存中获取<code>cookie</code>，如果缓存中不存在则调用<code>/login</code>接口，为了避免这一过程失败，允许最多重试3次。
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">public</span> String <span
                    class="hljs-title function_">getCookie</span><span class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    <span class="hljs-keyword">for</span> (<span
        class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span
        class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++) {</span>
<span class="code-block-extension-codeLine" data-line-num="3">        <span class="hljs-type">String</span> <span
        class="hljs-variable">cookieStr</span> <span class="hljs-operator">=</span> loginCookie.get(<span
        class="hljs-string">"XXL_JOB_LOGIN_IDENTITY"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="4">        <span class="hljs-keyword">if</span> (cookieStr !=<span
        class="hljs-literal">null</span>) {</span>
<span class="code-block-extension-codeLine" data-line-num="5">            <span class="hljs-keyword">return</span> <span
        class="hljs-string">"XXL_JOB_LOGIN_IDENTITY="</span>+cookieStr;</span>
<span class="code-block-extension-codeLine" data-line-num="6">        }</span>
<span class="code-block-extension-codeLine" data-line-num="7">        login();</span>
<span class="code-block-extension-codeLine" data-line-num="8">    }</span>
<span class="code-block-extension-codeLine" data-line-num="9">    <span class="hljs-keyword">throw</span> <span
        class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span
        class="hljs-string">"get xxl-job cookie error!"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="10">}</span>
</code></pre>
            <h4 data-id="heading-4">执行器接口</h4>
            <p>创建一个<code>JobGroupService</code>，根据<code>appName</code>和执行器名称<code>title</code>查询执行器列表：
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">public</span> List&lt;XxlJobGroup&gt; <span
                    class="hljs-title function_">getJobGroup</span><span class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    String url=adminAddresses+<span class="hljs-string">"/jobgroup/pageList"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="3">    <span class="hljs-type">HttpResponse</span> <span
        class="hljs-variable">response</span> <span class="hljs-operator">=</span> HttpRequest.post(url)</span>
<span class="code-block-extension-codeLine" data-line-num="4">            .form(<span
        class="hljs-string">"appname"</span>, appName)</span>
<span class="code-block-extension-codeLine" data-line-num="5">            .form(<span class="hljs-string">"title"</span>, title)</span>
<span class="code-block-extension-codeLine" data-line-num="6">            .cookie(jobLoginService.getCookie())</span>
<span class="code-block-extension-codeLine" data-line-num="7">            .execute();</span>
<span class="code-block-extension-codeLine" data-line-num="8"></span>
<span class="code-block-extension-codeLine" data-line-num="9">    <span class="hljs-type">String</span> <span
        class="hljs-variable">body</span> <span class="hljs-operator">=</span> response.body();</span>
<span class="code-block-extension-codeLine" data-line-num="10">    <span class="hljs-type">JSONArray</span> <span
        class="hljs-variable">array</span> <span class="hljs-operator">=</span> JSONUtil.parse(body).getByPath(<span
        class="hljs-string">"data"</span>, JSONArray.class);</span>
<span class="code-block-extension-codeLine" data-line-num="11">    List&lt;XxlJobGroup&gt; list = array.stream()</span>
<span class="code-block-extension-codeLine" data-line-num="12">            .map(o -&gt; JSONUtil.toBean((JSONObject) o, XxlJobGroup.class))</span>
<span class="code-block-extension-codeLine" data-line-num="13">            .collect(Collectors.toList());</span>
<span class="code-block-extension-codeLine" data-line-num="14">    <span class="hljs-keyword">return</span> list;</span>
<span class="code-block-extension-codeLine" data-line-num="15">}</span>
</code></pre>
            <p>我们在后面要根据配置文件中的<code>appName</code>和<code>title</code>判断当前执行器是否已经被注册到调度中心过，如果已经注册过那么则跳过，而<code>/jobgroup/pageList</code>接口是一个模糊查询接口，所以在查询列表的结果列表中，还需要再进行一次精确匹配。
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span
                    class="hljs-title function_">preciselyCheck</span><span class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine"
      data-line-num="2">    List&lt;XxlJobGroup&gt; jobGroup = getJobGroup();</span>
<span class="code-block-extension-codeLine"
      data-line-num="3">    Optional&lt;XxlJobGroup&gt; has = jobGroup.stream()</span>
<span class="code-block-extension-codeLine" data-line-num="4">            .filter(xxlJobGroup -&gt; xxlJobGroup.getAppname().equals(appName)</span>
<span class="code-block-extension-codeLine" data-line-num="5">                    &amp;&amp; xxlJobGroup.getTitle().equals(title))</span>
<span class="code-block-extension-codeLine" data-line-num="6">            .findAny();</span>
<span class="code-block-extension-codeLine" data-line-num="7">    <span class="hljs-keyword">return</span> has.isPresent();</span>
<span class="code-block-extension-codeLine" data-line-num="8">}</span>
</code></pre>
            <p>注册新<code>executor</code>到调度中心：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span
                    class="hljs-title function_">autoRegisterGroup</span><span class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    String url=adminAddresses+<span class="hljs-string">"/jobgroup/save"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="3">    <span class="hljs-type">HttpResponse</span> <span
        class="hljs-variable">response</span> <span class="hljs-operator">=</span> HttpRequest.post(url)</span>
<span class="code-block-extension-codeLine" data-line-num="4">            .form(<span
        class="hljs-string">"appname"</span>, appName)</span>
<span class="code-block-extension-codeLine" data-line-num="5">            .form(<span class="hljs-string">"title"</span>, title)</span>
<span class="code-block-extension-codeLine" data-line-num="6">            .cookie(jobLoginService.getCookie())</span>
<span class="code-block-extension-codeLine" data-line-num="7">            .execute();</span>
<span class="code-block-extension-codeLine" data-line-num="8">    <span class="hljs-type">Object</span> <span
        class="hljs-variable">code</span> <span class="hljs-operator">=</span> JSONUtil.parse(response.body()).getByPath(<span
        class="hljs-string">"code"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="9">    <span
        class="hljs-keyword">return</span> code.equals(<span class="hljs-number">200</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="10">}</span>
</code></pre>
            <h4 data-id="heading-5">任务接口</h4>
            <p>创建一个<code>JobInfoService</code>，根据执行器<code>id</code>，<code>jobHandler</code>名称查询任务列表，和上面一样，也是模糊查询：
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div
                    class="code-block-extension-headerRight"></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">public</span> List&lt;XxlJobInfo&gt; <span
                    class="hljs-title function_">getJobInfo</span><span class="hljs-params">(Integer jobGroupId,String executorHandler)</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    String url=adminAddresses+<span class="hljs-string">"/jobinfo/pageList"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="3">    <span class="hljs-type">HttpResponse</span> <span
        class="hljs-variable">response</span> <span class="hljs-operator">=</span> HttpRequest.post(url)</span>
<span class="code-block-extension-codeLine" data-line-num="4">            .form(<span
        class="hljs-string">"jobGroup"</span>, jobGroupId)</span>
<span class="code-block-extension-codeLine" data-line-num="5">            .form(<span class="hljs-string">"executorHandler"</span>, executorHandler)</span>
<span class="code-block-extension-codeLine" data-line-num="6">            .form(<span class="hljs-string">"triggerStatus"</span>, -<span
        class="hljs-number">1</span>)</span>
<span class="code-block-extension-codeLine" data-line-num="7">            .cookie(jobLoginService.getCookie())</span>
<span class="code-block-extension-codeLine" data-line-num="8">            .execute();</span>
<span class="code-block-extension-codeLine" data-line-num="9"></span>
<span class="code-block-extension-codeLine" data-line-num="10">    <span class="hljs-type">String</span> <span
        class="hljs-variable">body</span> <span class="hljs-operator">=</span> response.body();</span>
<span class="code-block-extension-codeLine" data-line-num="11">    <span class="hljs-type">JSONArray</span> <span
        class="hljs-variable">array</span> <span class="hljs-operator">=</span> JSONUtil.parse(body).getByPath(<span
        class="hljs-string">"data"</span>, JSONArray.class);</span>
<span class="code-block-extension-codeLine" data-line-num="12">    List&lt;XxlJobInfo&gt; list = array.stream()</span>
<span class="code-block-extension-codeLine" data-line-num="13">            .map(o -&gt; JSONUtil.toBean((JSONObject) o, XxlJobInfo.class))</span>
<span class="code-block-extension-codeLine" data-line-num="14">            .collect(Collectors.toList());</span>
<span class="code-block-extension-codeLine" data-line-num="15"></span>
<span class="code-block-extension-codeLine" data-line-num="16">    <span class="hljs-keyword">return</span> list;</span>
<span class="code-block-extension-codeLine" data-line-num="17">}</span>
</code></pre>
            <p>注册一个新任务，最终返回创建的新任务的<code>id</code>：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div
                    class="code-block-extension-headerRight"></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-keyword">public</span> Integer <span
                    class="hljs-title function_">addJobInfo</span><span
                    class="hljs-params">(XxlJobInfo xxlJobInfo)</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    String url=adminAddresses+<span class="hljs-string">"/jobinfo/add"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="3">    Map&lt;String, Object&gt; paramMap = BeanUtil.beanToMap(xxlJobInfo);</span>
<span class="code-block-extension-codeLine" data-line-num="4">    <span class="hljs-type">HttpResponse</span> <span
        class="hljs-variable">response</span> <span class="hljs-operator">=</span> HttpRequest.post(url)</span>
<span class="code-block-extension-codeLine" data-line-num="5">            .form(paramMap)</span>
<span class="code-block-extension-codeLine" data-line-num="6">            .cookie(jobLoginService.getCookie())</span>
<span class="code-block-extension-codeLine" data-line-num="7">            .execute();</span>
<span class="code-block-extension-codeLine" data-line-num="8"></span>
<span class="code-block-extension-codeLine" data-line-num="9">    <span class="hljs-type">JSON</span> <span
        class="hljs-variable">json</span> <span class="hljs-operator">=</span> JSONUtil.parse(response.body());</span>
<span class="code-block-extension-codeLine" data-line-num="10">    <span class="hljs-type">Object</span> <span
        class="hljs-variable">code</span> <span class="hljs-operator">=</span> json.getByPath(<span class="hljs-string">"code"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="11">    <span
        class="hljs-keyword">if</span> (code.equals(<span class="hljs-number">200</span>)){</span>
<span class="code-block-extension-codeLine" data-line-num="12">        <span class="hljs-keyword">return</span> Convert.toInt(json.getByPath(<span
        class="hljs-string">"content"</span>));</span>
<span class="code-block-extension-codeLine" data-line-num="13">    }</span>
<span class="code-block-extension-codeLine" data-line-num="14">    <span class="hljs-keyword">throw</span> <span
        class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(<span
        class="hljs-string">"add jobInfo error!"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="15">}</span>
</code></pre>
            <h3 data-id="heading-6">2、创建新注解</h3>
            <p>
                在创建任务时，必填字段除了执行器和<code>jobHandler</code>之外，还有<strong>任务描述</strong>、<strong>负责人</strong>、<strong>Cron表达式</strong>、<strong>调度类型</strong>、<strong>运行模式</strong>。在这里，我们默认调度类型为<code>CRON</code>、运行模式为<code>BEAN</code>，另外的3个字段的信息需要用户指定。
            </p>
            <p>因此我们需要创建一个新注解<code>@XxlRegister</code>，来配合原生的<code>@XxlJob</code>注解进行使用，填写这几个字段的信息：
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div><div data-v-159ebe90=""
                                                                     class="render"></div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-meta">@Target({ElementType.METHOD})</span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-meta">@Retention(RetentionPolicy.RUNTIME)</span></span>
<span class="code-block-extension-codeLine" data-line-num="3"><span class="hljs-keyword">public</span> <span
        class="hljs-meta">@interface</span> XxlRegister {</span>
<span class="code-block-extension-codeLine" data-line-num="4">    String <span
        class="hljs-title function_">cron</span><span class="hljs-params">()</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="5">    String <span
        class="hljs-title function_">jobDesc</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span
        class="hljs-string">"default jobDesc"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="6">    String <span
        class="hljs-title function_">author</span><span class="hljs-params">()</span> <span
        class="hljs-keyword">default</span> <span class="hljs-string">"default Author"</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="7">    <span class="hljs-type">int</span> <span
        class="hljs-title function_">triggerStatus</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span
        class="hljs-number">0</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="8">}</span>
</code></pre>
            <p>最后，额外添加了一个<code>triggerStatus</code>属性，表示任务的默认调度状态，0为停止状态，1为运行状态。</p>
            <h3 data-id="heading-7">3、自动注册核心</h3>
            <p>基本准备工作做完后，下面实现自动注册执行器和<code>jobHandler</code>的核心代码。核心类实现<code>ApplicationListener</code>接口，在接收到<code>ApplicationReadyEvent</code>事件后开始执行自动注册逻辑。
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"></div><div data-v-159ebe90=""
                                                                    class="render"> </div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-meta">@Component</span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-keyword">public</span> <span
        class="hljs-keyword">class</span> <span class="hljs-title class_">XxlJobAutoRegister</span> <span
        class="hljs-keyword">implements</span> <span class="hljs-title class_">ApplicationListener</span>&lt;ApplicationReadyEvent&gt;, </span>
<span class="code-block-extension-codeLine" data-line-num="3">        ApplicationContextAware {</span>
<span class="code-block-extension-codeLine" data-line-num="4">    <span class="hljs-keyword">private</span> <span
        class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span
        class="hljs-type">Log</span> <span class="hljs-variable">log</span> <span class="hljs-operator">=</span>LogFactory.get();</span>
<span class="code-block-extension-codeLine" data-line-num="5">    <span class="hljs-keyword">private</span> ApplicationContext applicationContext;</span>
<span class="code-block-extension-codeLine" data-line-num="6">    <span class="hljs-meta">@Autowired</span></span>
<span class="code-block-extension-codeLine" data-line-num="7">    <span class="hljs-keyword">private</span> JobGroupService jobGroupService;</span>
<span class="code-block-extension-codeLine" data-line-num="8">    <span class="hljs-meta">@Autowired</span></span>
<span class="code-block-extension-codeLine" data-line-num="9">    <span class="hljs-keyword">private</span> JobInfoService jobInfoService;</span>
<span class="code-block-extension-codeLine" data-line-num="10"></span>
<span class="code-block-extension-codeLine" data-line-num="11">    <span class="hljs-meta">@Override</span></span>
<span class="code-block-extension-codeLine" data-line-num="12">    <span class="hljs-keyword">public</span> <span
        class="hljs-keyword">void</span> <span class="hljs-title function_">setApplicationContext</span><span
        class="hljs-params">(ApplicationContext applicationContext)</span> <span class="hljs-keyword">throws</span> BeansException {</span>
<span class="code-block-extension-codeLine" data-line-num="13">        <span class="hljs-built_in">this</span>.applicationContext=applicationContext;</span>
<span class="code-block-extension-codeLine" data-line-num="14">    }</span>
<span class="code-block-extension-codeLine" data-line-num="15"></span>
<span class="code-block-extension-codeLine" data-line-num="16">    <span class="hljs-meta">@Override</span></span>
<span class="code-block-extension-codeLine" data-line-num="17">    <span class="hljs-keyword">public</span> <span
        class="hljs-keyword">void</span> <span class="hljs-title function_">onApplicationEvent</span><span
        class="hljs-params">(ApplicationReadyEvent event)</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="18">        addJobGroup();<span
        class="hljs-comment">//注册执行器</span></span>
<span class="code-block-extension-codeLine" data-line-num="19">        addJobInfo();<span
        class="hljs-comment">//注册任务</span></span>
<span class="code-block-extension-codeLine" data-line-num="20">    }</span>
<span class="code-block-extension-codeLine" data-line-num="21">}</span>
</code></pre>
            <p>自动注册执行器的代码非常简单，根据配置文件中的<code>appName</code>和<code>title</code>精确匹配查看调度中心是否已有执行器被注册过了，如果存在则跳过，不存在则新注册一个：
            </p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div><div data-v-159ebe90="" class="render"> </div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span
                    class="hljs-title function_">addJobGroup</span><span class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    <span class="hljs-keyword">if</span> (jobGroupService.preciselyCheck())</span>
<span class="code-block-extension-codeLine" data-line-num="3">        <span class="hljs-keyword">return</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="4"></span>
<span class="code-block-extension-codeLine" data-line-num="5">    <span class="hljs-keyword">if</span>(jobGroupService.autoRegisterGroup())</span>
<span class="code-block-extension-codeLine" data-line-num="6">        log.info(<span class="hljs-string">"auto register xxl-job group success!"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="7">}</span>
</code></pre>
            <p>自动注册任务的逻辑则相对复杂一些，需要完成：</p>
            <ul>
                <li>通过<code>applicationContext</code>拿到spring容器中的所有bean，再拿到这些bean中所有添加了<code>@XxlJob</code>注解的方法
                </li>
                <li>对上面获取到的方法进行检查，是否添加了我们自定义的<code>@XxlRegister</code>注解，如果没有则跳过，不进行自动注册
                </li>
                <li>
                    对同时添加了<code>@XxlJob</code>和<code>@XxlRegister</code>的方法，通过执行器id和<code>jobHandler</code>的值判断是否已经在调度中心注册过了，如果已存在则跳过
                </li>
                <li>对于满足注解条件且没有注册过的<code>jobHandler</code>，调用接口注册到调度中心</li>
            </ul>
            <p>具体代码如下：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div><div data-v-159ebe90=""
                                                                     class="render"></div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span
                    class="hljs-title function_">addJobInfo</span><span class="hljs-params">()</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="2">    List&lt;XxlJobGroup&gt; jobGroups = jobGroupService.getJobGroup();</span>
<span class="code-block-extension-codeLine" data-line-num="3">    <span class="hljs-type">XxlJobGroup</span> <span
        class="hljs-variable">xxlJobGroup</span> <span class="hljs-operator">=</span> jobGroups.get(<span
        class="hljs-number">0</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="4"></span>
<span class="code-block-extension-codeLine" data-line-num="5">    String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, <span
        class="hljs-literal">false</span>, <span class="hljs-literal">true</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="6">    <span class="hljs-keyword">for</span> (String beanDefinitionName : beanDefinitionNames) {</span>
<span class="code-block-extension-codeLine" data-line-num="7">        <span class="hljs-type">Object</span> <span
        class="hljs-variable">bean</span> <span class="hljs-operator">=</span> applicationContext.getBean(beanDefinitionName);</span>
<span class="code-block-extension-codeLine" data-line-num="8"></span>
<span class="code-block-extension-codeLine" data-line-num="9">        Map&lt;Method, XxlJob&gt; annotatedMethods  = MethodIntrospector.selectMethods(bean.getClass(),</span>
<span class="code-block-extension-codeLine" data-line-num="10">                <span
        class="hljs-keyword">new</span> <span class="hljs-title class_">MethodIntrospector</span>.MetadataLookup&lt;XxlJob&gt;() {</span>
<span class="code-block-extension-codeLine" data-line-num="11">                    <span
        class="hljs-meta">@Override</span></span>
<span class="code-block-extension-codeLine" data-line-num="12">                    <span
        class="hljs-keyword">public</span> XxlJob <span class="hljs-title function_">inspect</span><span
        class="hljs-params">(Method method)</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="13">                        <span
        class="hljs-keyword">return</span> AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);</span>
<span class="code-block-extension-codeLine" data-line-num="14">                    }</span>
<span class="code-block-extension-codeLine" data-line-num="15">                });</span>
<span class="code-block-extension-codeLine" data-line-num="16">        <span class="hljs-keyword">for</span> (Map.Entry&lt;Method, XxlJob&gt; methodXxlJobEntry : annotatedMethods.entrySet()) {</span>
<span class="code-block-extension-codeLine" data-line-num="17">            <span class="hljs-type">Method</span> <span
        class="hljs-variable">executeMethod</span> <span
        class="hljs-operator">=</span> methodXxlJobEntry.getKey();</span>
<span class="code-block-extension-codeLine" data-line-num="18">            <span class="hljs-type">XxlJob</span> <span
        class="hljs-variable">xxlJob</span> <span class="hljs-operator">=</span> methodXxlJobEntry.getValue();</span>
<span class="code-block-extension-codeLine" data-line-num="19"></span>
<span class="code-block-extension-codeLine" data-line-num="20">            <span class="hljs-comment">//自动注册</span></span>
<span class="code-block-extension-codeLine" data-line-num="21">            <span class="hljs-keyword">if</span> (executeMethod.isAnnotationPresent(XxlRegister.class)) {</span>
<span class="code-block-extension-codeLine" data-line-num="22">                <span
        class="hljs-type">XxlRegister</span> <span class="hljs-variable">xxlRegister</span> <span class="hljs-operator">=</span> executeMethod.getAnnotation(XxlRegister.class);</span>
<span class="code-block-extension-codeLine" data-line-num="23">                List&lt;XxlJobInfo&gt; jobInfo = jobInfoService.getJobInfo(xxlJobGroup.getId(), xxlJob.value());</span>
<span class="code-block-extension-codeLine" data-line-num="24">                <span class="hljs-keyword">if</span> (!jobInfo.isEmpty()){</span>
<span class="code-block-extension-codeLine" data-line-num="25">                    <span class="hljs-comment">//因为是模糊查询，需要再判断一次</span></span>
<span class="code-block-extension-codeLine" data-line-num="26">                    Optional&lt;XxlJobInfo&gt; first = jobInfo.stream()</span>
<span class="code-block-extension-codeLine" data-line-num="27">                            .filter(xxlJobInfo -&gt; xxlJobInfo.getExecutorHandler().equals(xxlJob.value()))</span>
<span class="code-block-extension-codeLine" data-line-num="28">                            .findFirst();</span>
<span class="code-block-extension-codeLine" data-line-num="29">                    <span class="hljs-keyword">if</span> (first.isPresent())</span>
<span class="code-block-extension-codeLine" data-line-num="30">                        <span class="hljs-keyword">continue</span>;</span>
<span class="code-block-extension-codeLine" data-line-num="31">                }</span>
<span class="code-block-extension-codeLine" data-line-num="32"></span>
<span class="code-block-extension-codeLine" data-line-num="33">                <span class="hljs-type">XxlJobInfo</span> <span
        class="hljs-variable">xxlJobInfo</span> <span class="hljs-operator">=</span> createXxlJobInfo(xxlJobGroup, xxlJob, xxlRegister);</span>
<span class="code-block-extension-codeLine" data-line-num="34">                <span
        class="hljs-type">Integer</span> <span class="hljs-variable">jobInfoId</span> <span
        class="hljs-operator">=</span> jobInfoService.addJobInfo(xxlJobInfo);</span>
<span class="code-block-extension-codeLine" data-line-num="35">            }</span>
<span class="code-block-extension-codeLine" data-line-num="36">        }</span>
<span class="code-block-extension-codeLine" data-line-num="37">    }</span>
<span class="code-block-extension-codeLine" data-line-num="38">}</span>
</code></pre>
            <h3 data-id="heading-8">4、自动装配</h3>
            <p>创建一个配置类，用于扫描<code>bean</code>：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div><div data-v-159ebe90="" class="render"></div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span
                    class="hljs-meta">@Configuration</span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-meta">@ComponentScan(basePackages = "com.xxl.job.plus.executor")</span></span>
<span class="code-block-extension-codeLine" data-line-num="3"><span class="hljs-keyword">public</span> <span
        class="hljs-keyword">class</span> <span class="hljs-title class_">XxlJobPlusConfig</span> {</span>
<span class="code-block-extension-codeLine" data-line-num="4">}</span>
</code></pre>
            <p>将它添加到<code>META-INF/spring.factories</code>文件：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"></div><div data-v-159ebe90="" class="render"></div></div></div><code
                    class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                    class="code-block-extension-codeLine" data-line-num="1">org.springframework.boot.autoconfigure.EnableAutoConfiguration=\</span>
<span class="code-block-extension-codeLine" data-line-num="2">  com.xxl.job.plus.executor.config.XxlJobPlusConfig</span>
</code></pre>
            <p>到这里<code>starter</code>的编写就完成了，可以通过maven发布jar包到本地或者私服：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">bash</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"></div><div data-v-159ebe90="" class="render"></div></div></div><code
                    class="hljs language-bash code-block-extension-codeShowNum" lang="bash"><span
                    class="code-block-extension-codeLine" data-line-num="1">mvn clean install/deploy</span>
</code></pre>
            <h2 data-id="heading-9">测试</h2>
            <p>新建一个springboot项目，引入我们在上面打好的包：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">xml</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"></div><div data-v-159ebe90=""
                                                                    class="render"> </div></div></div><code
                    class="hljs language-xml code-block-extension-codeShowNum" lang="xml"><span
                    class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-tag">&lt;<span
                    class="hljs-name">dependency</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="2">    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.cn.hydra<span
        class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="3">    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>xxljob-autoregister-spring-boot-starter<span
        class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="4">    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1<span
        class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span></span>
<span class="code-block-extension-codeLine" data-line-num="5"><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span></span>
</code></pre>
            <p>在<code>application.properties</code>中配置xxl-job的信息，首先是原生的配置内容：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">properties</span></div><div
                    class="code-block-extension-headerRight">
					</div></div><code
                    class="hljs language-properties code-block-extension-codeShowNum" lang="properties"><span
                    class="code-block-extension-codeLine" data-line-num="1">xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin</span>
<span class="code-block-extension-codeLine" data-line-num="2">xxl.job.accessToken=default_token</span>
<span class="code-block-extension-codeLine" data-line-num="3">xxl.job.executor.appname=xxl-job-executor-test</span>
<span class="code-block-extension-codeLine" data-line-num="4">xxl.job.executor.address=</span>
<span class="code-block-extension-codeLine" data-line-num="5">xxl.job.executor.ip=127.0.0.1</span>
<span class="code-block-extension-codeLine" data-line-num="6">xxl.job.executor.port=9999</span>
<span class="code-block-extension-codeLine"
      data-line-num="7">xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler</span>
<span class="code-block-extension-codeLine" data-line-num="8">xxl.job.executor.logretentiondays=30</span>
</code></pre>
            <p>此外还要额外添加我们自己的starter要求的新配置内容：</p>
            <pre><div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);"><div
                    class="code-block-extension-headerLeft"><div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">properties</span></div><div
                    class="code-block-extension-headerRight"><div data-v-4fdcfe21="" class="code-tips"> </div><div
                    data-v-159ebe90="" class="render"></div></div></div><code
                    class="hljs language-properties code-block-extension-codeShowNum" lang="properties"><span
                    class="code-block-extension-codeLine" data-line-num="1"># admin用户名</span>
<span class="code-block-extension-codeLine" data-line-num="2">xxl.job.admin.username=admin</span>
<span class="code-block-extension-codeLine" data-line-num="3"># admin 密码</span>
<span class="code-block-extension-codeLine" data-line-num="4">xxl.job.admin.password=123456</span>
<span class="code-block-extension-codeLine" data-line-num="5"># 执行器名称</span>
<span class="code-block-extension-codeLine" data-line-num="6">xxl.job.executor.title=test-title</span>
</code></pre>
            <p>完成后在代码中配置一下<code>XxlJobSpringExecutor</code>，然后在测试接口上添加原生<code>@XxlJob</code>注解和我们自定义的<code>@XxlRegister</code>注解：
            </p>
            <pre>
			<div class="code-block-extension-header" style="background-color: rgb(248, 248, 248);">
			<div
                    class="code-block-extension-headerLeft">
					<div class="code-block-extension-foldBtn"></div><span
                    class="code-block-extension-lang">java</span></div><div class="code-block-extension-headerRight"><div
                    data-v-4fdcfe21="" class="code-tips"> </div><div data-v-159ebe90=""
                                                                     class="render"></div></div></div>
					<code
                            class="hljs language-java code-block-extension-codeShowNum" lang="java"><span
                            class="code-block-extension-codeLine" data-line-num="1"><span class="hljs-meta">@XxlJob(value = "testJob")</span></span>
<span class="code-block-extension-codeLine" data-line-num="2"><span class="hljs-meta">@XxlRegister(cron = "0 0 0 * * ? *",</span></span>
<span class="code-block-extension-codeLine" data-line-num="3">        author = "hydra",</span>
<span class="code-block-extension-codeLine" data-line-num="4">        jobDesc = "测试job")</span>
<span class="code-block-extension-codeLine" data-line-num="5"><span class="hljs-keyword">public</span> <span
        class="hljs-keyword">void</span> <span class="hljs-title function_">testJob</span><span
        class="hljs-params">()</span>{</span>
<span class="code-block-extension-codeLine" data-line-num="6">    System.out.println(<span class="hljs-string">"#公众号：码农参上"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="7">}</span>
<span class="code-block-extension-codeLine" data-line-num="8"></span>
<span class="code-block-extension-codeLine" data-line-num="9"></span>
<span class="code-block-extension-codeLine" data-line-num="10"><span
        class="hljs-meta">@XxlJob(value = "testJob222")</span></span>
<span class="code-block-extension-codeLine" data-line-num="11"><span class="hljs-meta">@XxlRegister(cron = "59 1-2 0 * * ?",</span></span>
<span class="code-block-extension-codeLine" data-line-num="12">        triggerStatus = 1)</span>
<span class="code-block-extension-codeLine" data-line-num="13"><span class="hljs-keyword">public</span> <span
        class="hljs-keyword">void</span> <span class="hljs-title function_">testJob2</span><span
        class="hljs-params">()</span>{</span>
<span class="code-block-extension-codeLine" data-line-num="14">    System.out.println(<span class="hljs-string">"#作者：Hydra"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="15">}</span>
<span class="code-block-extension-codeLine" data-line-num="16"></span>
<span class="code-block-extension-codeLine" data-line-num="17"><span
        class="hljs-meta">@XxlJob(value = "testJob444")</span></span>
<span class="code-block-extension-codeLine" data-line-num="18"><span class="hljs-meta">@XxlRegister(cron = "59 59 23 * * ?")</span></span>
<span class="code-block-extension-codeLine" data-line-num="19"><span class="hljs-keyword">public</span> <span
        class="hljs-keyword">void</span> <span class="hljs-title function_">testJob4</span><span
        class="hljs-params">()</span>{</span>
<span class="code-block-extension-codeLine" data-line-num="20">    System.out.println(<span class="hljs-string">"hello xxl job"</span>);</span>
<span class="code-block-extension-codeLine" data-line-num="21">}</span>
</code></pre>
            <p>启动项目，可以看到执行器自动注册成功：</p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d4ba662c1acb4b1d92ef69c4788db9f1~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>再打开调度中心的任务管理页面，可以看到同时添加了两个注解的任务也已经自动完成了注册：</p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0044c46514d54e4d8b09954188c9fe5d~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>从页面上手动执行任务进行测试，可以执行成功：</p>
            <p>
                <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f9f14a42d0104411857ee1a9ebdf5faa~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp"
                     alt="" loading="lazy" class="medium-zoom-image"></p>
            <p>到这里，starter的编写和测试过程就算基本完成了，项目中引入后，以后也能省出更多的时间来
                <del>摸鱼</del>
                学习了~
            </p>
            <h2 data-id="heading-10">最后</h2>
            <p>项目的完整代码已经传到了我的github上，码字不易,也欢迎来给我点个star支持一下,感谢~</p>
            <blockquote>
                <p><a href="https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Ftrunks2008%2Fxxl-job-auto-register"
                      target="_blank" title="https://github.com/trunks2008/xxl-job-auto-register"
                      ref="nofollow noopener noreferrer">github.com/trunks2008/…</a></p>
            </blockquote>
            <p>那么，这次的分享就到这里，我是Hydra，我们下篇再见。</p>
            <blockquote>
                <p>作者简介，<code>码农参上</code>，一个热爱分享的公众号，有趣、深入、直接，与你聊聊技术。欢迎添加好友，进一步交流。
                </p>
            </blockquote>
            <hr>
            <p><em><strong>本文正在参加<a href="https://juejin.cn/post/7207698564641996856/"
                                          title="https://juejin.cn/post/7207698564641996856/"
                                          target="_blank">「金石计划」</a></strong></em></p></div>
    </div>
</article>
</html>